From 97008a219a1f3f96416d4d7c78b3482b8840c838 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=98yvind=20Kol=C3=A5s?= Date: Fri, 24 Aug 2018 19:16:34 +0200 Subject: [PATCH] base: preserve color in premutiplied alpha Alpha values below BABL_ALPHA_FLOOR gets stored in the components as BABL_ALPHA_FLOOR with alpha also set to BABL_ALPHA_FLOOR, recovery from pre-multiplied to non-premultiplied is done as normal. BABL_FLOOR is set to be 1/65536.0 with a hope that this gives us approximately 8bit of color data preserved when using half-float. Upon encountering exactly BABL_ALPHA_FLOOR in premultiplied conversion to non-premultiplied interprets this as 0.0, making pure 0.0 values round trip correctly from RGBA to RaGaBaA back to RGBA. This makes a lot of extensions invalid - as expected but it already allows testing the behavior of GEGL and GIMP with this additional feature from babl. --- babl/babl-internal.h | 1 + babl/base/babl-base.h | 1 + babl/base/model-gray.c | 77 ++++++++++++++++------------ babl/base/model-rgb.c | 114 ++++++++++++++++++++++++----------------- babl/base/util.h | 5 +- 5 files changed, 115 insertions(+), 83 deletions(-) diff --git a/babl/babl-internal.h b/babl/babl-internal.h index 0f8a07c..8f31434 100644 --- a/babl/babl-internal.h +++ b/babl/babl-internal.h @@ -30,6 +30,7 @@ #define BABL_MAX_COMPONENTS 32 #define BABL_CONVERSIONS 5 + #include #include #include diff --git a/babl/base/babl-base.h b/babl/base/babl-base.h index 80a5b54..bc67f5c 100644 --- a/babl/base/babl-base.h +++ b/babl/base/babl-base.h @@ -19,6 +19,7 @@ #ifndef _BABL_BASE_H #define _BABL_BASE_H + void babl_base_init (void); void babl_base_destroy (void); void babl_formats_init (void); diff --git a/babl/base/model-gray.c b/babl/base/model-gray.c index 9d3677b..eea9ebf 100644 --- a/babl/base/model-gray.c +++ b/babl/base/model-gray.c @@ -425,16 +425,16 @@ gray_alpha_premultiplied_to_rgba (Babl *conversion, while (n--) { double luminance = *(double *) src[0]; - double alpha = *(double *) src[1]; - - if (alpha > BABL_ALPHA_THRESHOLD) - { - luminance = luminance / alpha; - } + double alpha; + alpha = *(double *) src[1]; + if (alpha == 0) + luminance = 0; else - { - luminance = 0.0; - } + { + luminance = luminance / alpha; + if (alpha == BABL_ALPHA_FLOOR) + alpha = 0.0; + } *(double *) dst[0] = luminance; *(double *) dst[1] = luminance; @@ -469,17 +469,20 @@ rgba_to_gray_alpha_premultiplied (Babl *conversion, double red = *(double *) src[0]; double green = *(double *) src[1]; double blue = *(double *) src[2]; - double alpha = *(double *) src[3]; double luminance; + double alpha, alpha_used; + alpha_used = alpha = *(double *) src[3]; + if (alpha < BABL_ALPHA_FLOOR) + alpha_used = BABL_ALPHA_FLOOR; luminance = red * RGB_LUMINANCE_RED + green * RGB_LUMINANCE_GREEN + blue * RGB_LUMINANCE_BLUE; - luminance *= alpha; + luminance *= alpha_used; *(double *) dst[0] = luminance; - *(double *) dst[1] = alpha; + *(double *) dst[1] = alpha_used; BABL_PLANAR_STEP } } @@ -498,15 +501,18 @@ non_premultiplied_to_premultiplied (Babl *conversion, while (n--) { - double alpha; int band; + double alpha, alpha_used; + + alpha_used = alpha = *(double *) src[src_bands-1]; + if (alpha < BABL_ALPHA_FLOOR) + alpha_used = BABL_ALPHA_FLOOR; - alpha = *(double *) src[src_bands - 1]; for (band = 0; band < src_bands - 1; band++) { - *(double *) dst[band] = *(double *) src[band] * alpha; + *(double *) dst[band] = *(double *) src[band] * alpha_used; } - *(double *) dst[dst_bands - 1] = alpha; + *(double *) dst[dst_bands - 1] = alpha_used; BABL_PLANAR_STEP } @@ -526,21 +532,19 @@ premultiplied_to_non_premultiplied (Babl *conversion, while (n--) { - double alpha; int band; + double alpha; + alpha = *(double *) src[src_bands-1]; - alpha = *(double *) src[src_bands - 1]; for (band = 0; band < src_bands - 1; band++) { - if (alpha > BABL_ALPHA_THRESHOLD) - { - *(double *) dst[band] = *(double *) src[band] / alpha; - } + if (alpha == 0.0) + *(double *) dst[band] = 0; else - { - *(double *) dst[band] = 0.0; - } + *(double *) dst[band] = *(double *) src[band] / alpha; } + if (alpha == BABL_ALPHA_FLOOR) + alpha = 0.0; *(double *) dst[dst_bands - 1] = alpha; BABL_PLANAR_STEP @@ -564,18 +568,20 @@ rgba2gray_nonlinear_premultiplied (Babl *conversion, double red = ((double *) src)[0]; double green = ((double *) src)[1]; double blue = ((double *) src)[2]; - double alpha = ((double *) src)[3]; - double luminance; double luma; + double alpha, alpha_used; + alpha_used = alpha = ((double *) src)[3]; + if (alpha < BABL_ALPHA_FLOOR) + alpha_used = BABL_ALPHA_FLOOR; luminance = red * RGB_LUMINANCE_RED + green * RGB_LUMINANCE_GREEN + blue * RGB_LUMINANCE_BLUE; luma = babl_trc_from_linear (trc, luminance); - ((double *) dst)[0] = luma * alpha; - ((double *) dst)[1] = alpha; + ((double *) dst)[0] = luma * alpha_used; + ((double *) dst)[1] = alpha_used; src += 4 * sizeof (double); dst += 2 * sizeof (double); @@ -595,16 +601,19 @@ gray_nonlinear_premultiplied2rgba (Babl *conversion, while (n--) { double luma = ((double *) src)[0]; - double alpha = ((double *) src)[1]; double luminance; - - if (alpha > BABL_ALPHA_THRESHOLD) - luma = luma / alpha; + double alpha; + alpha = ((double *) src)[1]; + if (alpha == 0.0) + luma = 0.0; else - luma = 0.0; + luma = luma / alpha; luminance = babl_trc_to_linear (trc, luma); + if (alpha == BABL_ALPHA_FLOOR) + alpha = 0.0; + ((double *) dst)[0] = luminance; ((double *) dst)[1] = luminance; ((double *) dst)[2] = luminance; diff --git a/babl/base/model-rgb.c b/babl/base/model-rgb.c index 66e16ab..c2c46f2 100644 --- a/babl/base/model-rgb.c +++ b/babl/base/model-rgb.c @@ -306,6 +306,7 @@ g3_nonlinear_to_linear (Babl *conversion, } } + static void non_premultiplied_to_premultiplied (Babl *conversion, int src_bands, @@ -322,14 +323,18 @@ non_premultiplied_to_premultiplied (Babl *conversion, while (n--) { double alpha; + double alpha_used; int band; - alpha = *(double *) src[src_bands - 1]; + alpha_used = alpha = *(double *) src[src_bands - 1]; + if (alpha < BABL_ALPHA_FLOOR) + alpha_used = BABL_ALPHA_FLOOR; + for (band = 0; band < src_bands - 1; band++) { - *(double *) dst[band] = *(double *) src[band] * alpha; + *(double *) dst[band] = *(double *) src[band] * alpha_used; } - *(double *) dst[dst_bands - 1] = alpha; + *(double *) dst[dst_bands - 1] = alpha_used; BABL_PLANAR_STEP } @@ -351,20 +356,21 @@ premultiplied_to_non_premultiplied (Babl *conversion, while (n--) { double alpha; + double recip_alpha; int band; alpha = *(double *) src[src_bands - 1]; - if (alpha > BABL_ALPHA_THRESHOLD) - { - double recip_alpha = 1.0 / alpha; - for (band = 0; band < src_bands - 1; band++) - *(double *) dst[band] = *(double *) src[band] * recip_alpha; - } + if (alpha == 0.0) + recip_alpha = 0.0; else - { - for (band = 0; band < src_bands - 1; band++) - *(double *) dst[band] = 0.0; - } + { + recip_alpha = 1.0 / alpha; + if (alpha == BABL_ALPHA_FLOOR) + alpha = 0.0; // making 0 round-trip to zero, causing discontinuity + } + + for (band = 0; band < src_bands - 1; band++) + *(double *) dst[band] = *(double *) src[band] * recip_alpha; *(double *) dst[dst_bands - 1] = alpha; BABL_PLANAR_STEP @@ -384,11 +390,14 @@ rgba2rgba_nonlinear_premultiplied (Babl *conversion, while (n--) { - double alpha = ((double *) src)[3]; - ((double *) dst)[0] = babl_trc_from_linear (trc[0], ((double *) src)[0]) * alpha; - ((double *) dst)[1] = babl_trc_from_linear (trc[1], ((double *) src)[1]) * alpha; - ((double *) dst)[2] = babl_trc_from_linear (trc[2], ((double *) src)[2]) * alpha; - ((double *) dst)[3] = alpha; + double alpha, alpha_used; + alpha_used = alpha = ((double *) src)[3]; + if (alpha < BABL_ALPHA_FLOOR) + alpha_used = BABL_ALPHA_FLOOR; + ((double *) dst)[0] = babl_trc_from_linear (trc[0], ((double *) src)[0]) * alpha_used; + ((double *) dst)[1] = babl_trc_from_linear (trc[1], ((double *) src)[1]) * alpha_used; + ((double *) dst)[2] = babl_trc_from_linear (trc[2], ((double *) src)[2]) * alpha_used; + ((double *) dst)[3] = alpha_used; src += 4 * sizeof (double); dst += 4 * sizeof (double); } @@ -407,19 +416,22 @@ rgba_nonlinear_premultiplied2rgba (Babl *conversion, while (n--) { - double alpha = ((double *) src)[3]; - if (alpha > BABL_ALPHA_THRESHOLD) - { + double alpha; + alpha = ((double *) src)[3]; + if (alpha == 0) + { + ((double *) dst)[0] = 0; + ((double *) dst)[1] = 0; + ((double *) dst)[2] = 0; + } + else + { ((double *) dst)[0] = babl_trc_to_linear (trc[0], ((double *) src)[0] / alpha); ((double *) dst)[1] = babl_trc_to_linear (trc[1], ((double *) src)[1] / alpha); ((double *) dst)[2] = babl_trc_to_linear (trc[2], ((double *) src)[2] / alpha); - } - else - { - ((double *) dst)[0] = 0.0; - ((double *) dst)[1] = 0.0; - ((double *) dst)[2] = 0.0; - } + if (alpha == BABL_ALPHA_FLOOR) + alpha = 0; + } ((double *) dst)[3] = alpha; src += 4 * sizeof (double); @@ -547,11 +559,14 @@ rgba2rgba_perceptual_premultiplied (Babl *conversion, while (n--) { - double alpha = ((double *) src)[3]; - ((double *) dst)[0] = babl_trc_from_linear (trc, ((double *) src)[0]) * alpha; - ((double *) dst)[1] = babl_trc_from_linear (trc, ((double *) src)[1]) * alpha; - ((double *) dst)[2] = babl_trc_from_linear (trc, ((double *) src)[2]) * alpha; - ((double *) dst)[3] = alpha; + double alpha, alpha_used; + alpha_used = alpha = ((double *) src)[3]; + if (alpha < BABL_ALPHA_FLOOR) + alpha_used = BABL_ALPHA_FLOOR; + ((double *) dst)[0] = babl_trc_from_linear (trc, ((double *) src)[0]) * alpha_used; + ((double *) dst)[1] = babl_trc_from_linear (trc, ((double *) src)[1]) * alpha_used; + ((double *) dst)[2] = babl_trc_from_linear (trc, ((double *) src)[2]) * alpha_used; + ((double *) dst)[3] = alpha_used; src += 4 * sizeof (double); dst += 4 * sizeof (double); } @@ -560,28 +575,31 @@ rgba2rgba_perceptual_premultiplied (Babl *conversion, static void rgba_perceptual_premultiplied2rgba (Babl *conversion, - char *src, - char *dst, - long samples) + char *src, + char *dst, + long samples) { const Babl *trc = perceptual_trc; long n = samples; while (n--) { - double alpha = ((double *) src)[3]; - if (alpha > BABL_ALPHA_THRESHOLD) - { - ((double *) dst)[0] = babl_trc_to_linear (trc, ((double *) src)[0] / alpha); - ((double *) dst)[1] = babl_trc_to_linear (trc, ((double *) src)[1] / alpha); - ((double *) dst)[2] = babl_trc_to_linear (trc, ((double *) src)[2] / alpha); - } + double alpha; + alpha = ((double *) src)[3]; + if (alpha == 0) + { + ((double *) dst)[0] = 0; + ((double *) dst)[1] = 0; + ((double *) dst)[2] = 0; + } else - { - ((double *) dst)[0] = 0.0; - ((double *) dst)[1] = 0.0; - ((double *) dst)[2] = 0.0; - } + { + ((double *) dst)[0] = babl_trc_to_linear (trc, ((double *) src)[0] / alpha); + ((double *) dst)[1] = babl_trc_to_linear (trc, ((double *) src)[1] / alpha); + ((double *) dst)[2] = babl_trc_to_linear (trc, ((double *) src)[2] / alpha); + if (alpha == BABL_ALPHA_FLOOR) + alpha = 0.0; + } ((double *) dst)[3] = alpha; src += 4 * sizeof (double); diff --git a/babl/base/util.h b/babl/base/util.h index b30c935..4d9f536 100644 --- a/babl/base/util.h +++ b/babl/base/util.h @@ -24,12 +24,15 @@ #include "pow-24.h" /* Alpha threshold used in the reference implementation for - * un-pre-multiplication of color data: + * un-pre-multiplication of color data, deprecated in favor of the following * * 0.01 / (2^16 - 1) */ #define BABL_ALPHA_THRESHOLD 0.000000152590219 + /* values below this are stored premultiplied with this value */ +#define BABL_ALPHA_FLOOR (1/65536.0) + #define BABL_PLANAR_SANITY \ { \ assert(src_bands>0); \ -- 2.30.2